﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;
using System.IO;
using System.Text;
using System.Xml;
using System.Web.UI;
using System.Data.SqlClient;
using System.Data;


namespace WebQueryRole
{
    public class BPAuthentication
    {
        
        public static LoginState IsLoggedIn(Page page, DBCollection dbCollection)
        {
            // read cookie, get username and auth token.
            try
            {
                if (page.Request.Cookies["session"] == null ||
                    page.Request.Cookies["session"]["username"] == null ||
                    page.Request.Cookies["session"]["token"] == null)
                {
                    RedirectLogin(page);
                    return LoginState.UNKNOWN_USER;
                }
                string username = page.Request.Cookies["session"]["username"];
                string token = page.Request.Cookies["session"]["token"];

                // check stored sessions for username / auth token
                SqlCommand cmd = new SqlCommand("sp_wqe_auth_token", dbCollection["web"]);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add("@username", SqlDbType.VarChar).Value = username;
                cmd.Parameters.Add("@token", SqlDbType.VarChar).Value = token;

                int found = int.Parse(cmd.ExecuteScalar().ToString());
                if (found > 0)
                {
                    return new LoginState(username);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            // not found, redirect page to login, append URL for visiting
            RedirectLogin(page);
            return LoginState.UNKNOWN_USER;
        }

        private static void RedirectLogin(Page page)
        {
            // if we are not at the page
            if (!page.Request.Url.ToString().Contains("Login.aspx"))
            {
                page.Response.BufferOutput = true;                
                page.Response.Redirect("Login.aspx?url=" + page.Request.Url.Segments[1]);
            }
        }

        /// <summary>
        /// Attempt to log in, if login fails, this method will redirect the user's browser to the login page
        /// and return false.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="dbCollection"></param>
        /// <returns></returns>
        public static LoginState LogIn(Page page, string username, string password, DBCollection dbCollection)
        {            
            try
            {
                // first attempt to check to see if we are a valid user
                string passEnc = DESEncrypt(username + password);
                SqlCommand cmd = new SqlCommand("sp_wqe_auth_user", dbCollection["web"]);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add("@username", SqlDbType.VarChar).Value = username;
                cmd.Parameters.Add("@pass", SqlDbType.VarChar).Value = passEnc;

                int found = 0;
                int.TryParse(cmd.ExecuteScalar().ToString(), out found);

                // NOT FOUND
                if (found == 0)
                {
                    return LoginState.UNKNOWN_USER;
                }

                // create a new token and save cookie
                // token: username + timestamp encrytped 
                string token = DESEncrypt(username + DateTime.Now.Millisecond);

                // if successful create a token
                cmd = new SqlCommand("sp_wqe_create_session", dbCollection["web"]);
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@username", SqlDbType.VarChar).Value = username;
                cmd.Parameters.Add("@token", SqlDbType.VarChar).Value = token;

                cmd.ExecuteNonQuery();

                // save cookie into browser
                page.Response.Cookies["session"]["username"] = username;
                page.Response.Cookies["session"]["token"] = token;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return LoginState.UNKNOWN_USER;
            }

            return new LoginState(username);
        }

        /// <summary>
        /// Create a new user and password
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="dbCollection"></param>
        /// <returns></returns>
        public static bool CreateUser(string username, string password, DBCollection dbCollection)
        {
            string passEnc = DESEncrypt(username + password);

            SqlCommand cmd = new SqlCommand("sp_wqe_create_user", dbCollection["web"]);
            cmd.CommandType = System.Data.CommandType.StoredProcedure;

            cmd.Parameters.Add("@username", SqlDbType.VarChar).Value = username;
            cmd.Parameters.Add("@pass", SqlDbType.VarChar).Value = passEnc;

            try
            {
                cmd.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
            return true;
        }

        /// Cryptography code        
        const string DESKey = "AQWSEDRF";
        const string DESIV = "HGFEDCBA";


        private static string DESDecrypt(string stringToDecrypt)//Decrypt the content
        {

            byte[] key;
            byte[] IV;

            byte[] inputByteArray;
            try
            {

                key = Convert2ByteArray(DESKey);

                IV = Convert2ByteArray(DESIV);

                int len = stringToDecrypt.Length; inputByteArray = Convert.FromBase64String(stringToDecrypt);


                DESCryptoServiceProvider des = new DESCryptoServiceProvider();

                MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(key, IV), CryptoStreamMode.Write);
                cs.Write(inputByteArray, 0, inputByteArray.Length);

                cs.FlushFinalBlock();

                Encoding encoding = Encoding.UTF8; return encoding.GetString(ms.ToArray());
            }

            catch (System.Exception ex)
            {

                throw ex;
            }

        }

        private static string DESEncrypt(string stringToEncrypt)// Encrypt the content
        {

            byte[] key;
            byte[] IV;

            byte[] inputByteArray;
            try
            {

                key = Convert2ByteArray(DESKey);

                IV = Convert2ByteArray(DESIV);

                inputByteArray = Encoding.UTF8.GetBytes(stringToEncrypt);
                DESCryptoServiceProvider des = new DESCryptoServiceProvider();

                MemoryStream ms = new MemoryStream(); CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(key, IV), CryptoStreamMode.Write);
                cs.Write(inputByteArray, 0, inputByteArray.Length);

                cs.FlushFinalBlock();

                return Convert.ToBase64String(ms.ToArray());
            }

            catch (System.Exception ex)
            {

                throw ex;
            }

        }

        private static byte[] Convert2ByteArray(string strInput)
        {

            int intCounter; char[] arrChar;
            arrChar = strInput.ToCharArray();

            byte[] arrByte = new byte[arrChar.Length];

            for (intCounter = 0; intCounter <= arrByte.Length - 1; intCounter++)
                arrByte[intCounter] = Convert.ToByte(arrChar[intCounter]);

            return arrByte;
        }
    }


    public class LoginState
    {        
        private const string UNKNOWN = "UNKNOWN";
        public static LoginState UNKNOWN_USER = new LoginState(UNKNOWN);
        private string _userName;
        
        public LoginState(string username) {
            _userName = username;
        }

        public string UserName { get { return _userName; } }
        public bool IsLoggedIn { get { return !_userName.Equals(UNKNOWN); } }
    }
}
